package com.kqzz.common.dynamicDB.config;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.core.NamedThreadLocal;

import com.kqzz.common.utils.ApplicationContextAwareUtil;

import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DynamicDataSourceContextHolder {
	/**
     * 存储已经注册的数据源的key
     */
    public static List<String> dataSourceIds = new ArrayList<>();
    
    private static Map<Object, Object> dataSources = new HashMap<>();

    /**
     * 线程级别的私有变量
     * <pre>
     * 为了支持嵌套切换，如ABC三个service都是不同的数据源
     * 其中A的某个业务要调B的方法，B的方法需要调用C的方法。一级一级调用切换，形成了链。
     * 传统的只设置当前线程的方式不能满足此业务需求，必须使用栈，后进先出。
     * </pre>
     */
    private static final ThreadLocal<Deque<String>> HOLDER = new NamedThreadLocal<Deque<String>>("dynamicDataSource"){
        @Override
        protected Deque<String> initialValue() {
            return new ArrayDeque<>();
        }
    };

    public static String getDataSourceRouterKey () {
        return HOLDER.get().peek();
    }

    /**
     * @Descripton 设置当前数据源 (如非必要不要手动调用，调用后确保最终清除)
     * @param dataSourceRouterKey
     * @author lipeng
     * @date 2021年11月18日 下午3:42:12
     * 
     * @update_Desc
     * @update_Author
     * @update_Date
     */
    public static void setDataSourceRouterKey (String dataSourceRouterKey) {
        log.info("切换至{}数据源", dataSourceRouterKey);
        String dataSourceStr = StringUtils.isEmpty(dataSourceRouterKey) ? "" : dataSourceRouterKey;
        HOLDER.get().push(dataSourceStr);
    }

    /**
     * 动态添加数据源
     *
     * @param name       数据源的key
     * @param dataSource 数据源对象
     */
    public static void addDataSource(String name, DataSource dataSource) {
    	DynamicRoutingDataSource dynamicDataSource = ApplicationContextAwareUtil.getBean(DynamicRoutingDataSource.class);
    	
    	if(CollUtil.isEmpty(dataSources)){
    		Map<Object,DataSource> resolvedDataSources = dynamicDataSource.getResolvedDataSources();
    		for (Map.Entry<Object,DataSource> entry : resolvedDataSources.entrySet()) {
        		dataSources.put(entry.getKey(), entry.getValue());
        	}
    	}
    	
    	dataSources.put(name, dataSource);
        dynamicDataSource.setTargetDataSources(dataSources);
        dynamicDataSource.afterPropertiesSet();
        
        dataSourceIds.add(name);
        
        log.info("成功添加数据源:{}",name);
    }
    
    /**
     * 设置数据源之前一定要先移除
     * 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称
     */
    public static void removeDataSourceRouterKey () {
    	Deque<String> deque = HOLDER.get();
        deque.poll();
        if (deque.isEmpty()) {
        	HOLDER.remove();
        }
    }

    /**
     * 判断指定DataSrouce当前是否存在
     *
     * @param dataSourceId
     * @return
     */
    public static boolean containsDataSource(String dataSourceId){
        return dataSourceIds.contains(dataSourceId);
    }

    /**
     * 强制清空本地线程
     * <p>
     * 防止内存泄漏，如手动调用了push可调用此方法确保清除
     * </p>
     */
    public static void clear() {
    	HOLDER.remove();
    }
}
